import java.util.Arrays;

public class Solution2 {

    // 废弃

    public int superEggDrop(int K, int N) {
        // dp[i][j]：一共有 i 层楼梯的情况下，使用 j 个鸡蛋的最少仍的次数
        // 注意：
        // 1、i 表示的是楼层的大小，不是第几层的意思，例如楼层区间 [8, 9, 10] 的大小为 3，这一点是在状态转移的过程中调整的定义
        // 2、j 表示可以使用的鸡蛋的个数，它是约束条件，我个人习惯放在后面的维度，表示消除后效性的意思

        // 0 个楼层和 0 个鸡蛋的情况都需要算上去，虽然没有实际的意义，但是作为递推的起点，被其它状态值所参考
        int[][] dp = new int[N + 1][K + 1];

        // 由于求的是最小值，因此初始化的时候赋值为一个较大的数，9999 是我试出来的
        for (int i = 0; i <= N; i++) {
            Arrays.fill(dp[i], i);
        }

        // 初始化：填写下标为 0、1 的行和下标为 0、1 的列
        // 第 0 行：楼层为 0 的时候，不管鸡蛋个数多少，都测试不出鸡蛋的 F 值，故全为 0
        for (int j = 0; j <= K; j++) {
            dp[0][j] = 0;
        }

        // 第 1 行：楼层为 1 的时候，0 个鸡蛋的时候，扔 0 次，1 个以及 1 个鸡蛋以上只需要扔 1 次
        dp[1][0] = 0;
        for (int j = 1; j <= K; j++) {
            dp[1][j] = 1;
        }


        // 第 0 列：鸡蛋个数为 0 的时候，不管楼层为多少，也测试不出鸡蛋的 F 值，故全为 0
        // 第 1 列：鸡蛋个数为 1 的时候，这是一种极端情况，要试出 F 值，最少次数就等于楼层高度（想想复杂度的定义）
        for (int i = 0; i <= N; i++) {
            dp[i][0] = 0;
            dp[i][1] = i;
        }

        // 从下标为 2 的行，下标为 2 的列开始填表
        for (int i = 2; i <= N; i++) {
            for (int j = 2; j <= K; j++) {
                // 在区间 [1, i] 里确定一个最低值
                int left = 1;
                int right = i;
                while (left < right) {
                    // 找绿线大于红线的第 1 个值
                    int mid = left + (right - left + 1) / 2;

                    int breakCount = dp[mid - 1][j - 1];
                    int notBreakCount = dp[i - mid][j];

                    if (breakCount > notBreakCount) {
                        // 当前 mid 一定不是解，解在 [left, mid - 1] 这个区间
                        right = mid - 1;
                    } else {
                        // 解在上面的反面区间，即 [mid, right] 这个区间
                        // 看到 left = mid; 取中间数上取整，原因见第 35 题题解
                        left = mid;
                    }
                }

                // 有两个候选值，选最小的那一个，也就是把 left 和 left + 1 代入状态转移方程
                int candidate1 = Math.max(dp[left - 1][j - 1], dp[i - left][j]);
                int candidate2 = Math.max(dp[left][j - 1], dp[i - left - 1][j]);
                dp[i][j] = Math.min(candidate1, candidate2) + 1;

                // 注释放在这里是由于本来这个解法就是下面解法的优化，写二分查找的过程中参考了下面的状态转移
//                for (int k = 1; k <= i; k++) {
//                    // 碎了，就需要往低层继续扔：层数少 1 ，鸡蛋也少 1
//                    // 不碎，就需要往高层继续扔：层数是当前层到最高层的距离差，鸡蛋数量不少
//                    // 两种情况都做了一次尝试，所以加 1
//                    dp[i][j] = Math.min(dp[i][j], Math.max(dp[k - 1][j - 1], dp[i - k][j]) + 1);
//                }
            }
        }

        System.out.println("测试");
        for (int i = 0; i < dp.length; i++) {
            System.out.println(Arrays.toString(dp[i]));
        }
        return dp[N][K];
    }

    public static void main(String[] args) {
        Solution2 solution = new Solution2();
//        int K = 1;
//        int N = 2;

//        int K = 2;
//        int N = 6;

//        int K = 3;
//        int N = 14;


//        int K = 8;
//        int N = 5000;

//        int K = 8;
//        int N = 10000;

//        int K = 9;
//        int N = 5000;

//        int K = 9;
//        int N = 10000;


        int K = 10;
        int N = 1000;


//        int K = 10;
//        int N = 10000;

//        int K = 13;
//        int N = 8192;

        int res = solution.superEggDrop(K, N);
        System.out.println(res);
    }
}
